package com.rexense.beacon.alg.weighted;

import com.rexense.beacon.alg.BleBase;
import com.rexense.beacon.alg.Trilateral;
import com.rexense.beacon.entity.Beacon;
import com.rexense.beacon.entity.Location;
import com.rexense.beacon.entity.Msg;

import java.util.*;
import java.util.stream.Collectors;

public class WeightedTrilateral {

    public static Location locate(List<Msg> msgList, Map<String, Beacon> beaconMap) {
        if (msgList == null || msgList.size() < 3) {
            throw new IllegalArgumentException("参数不合法，msgList.size >= 3");
        }

        msgList.sort(Comparator.comparing(Msg::getRssi).reversed());

        List<BleBase> baseList =
                msgList.stream().map(msg -> {
                    Beacon beacon = beaconMap.get(msg.getBeaconId());
                    return BleBase.builder()
                            .beaconId(msg.getBeaconId())
                            .rssi(msg.getRssi())
                            .h(beacon.getH())
                            .n(beacon.getN())
                            .p0(beacon.getP0())
                            .x(beacon.getX())
                            .y(beacon.getY())
                            .build()
                            .distinct();
                }).collect(Collectors.toList());

        int len = baseList.size();

        double totalWeight = .0;

        double x = .0;
        double y = .0;

        for (int i = 0; i < len - 2; i++) {
            for (int j = i + 1; j < len - 1; j++) {
                for (int k = j + 1; k < len; k++) {
                    List<BleBase> bleBases = new ArrayList<>(Arrays.asList(baseList.get(i), baseList.get(j), baseList.get(k)));
                    Location locate = Trilateral.locate(bleBases);
                    List<Double> weightList = new ArrayList<>(
                            Arrays.asList(
                                    1.0 / bleBases.get(0).getDistinct(),
                                    1.0 / bleBases.get(1).getDistinct(),
                                    1.0 / bleBases.get(2).getDistinct()
                            )
                    );
                    double currentTotalWeight = weightList.stream().reduce(Double::sum).orElseGet(() -> 0.);
                    x += locate.getX() * currentTotalWeight;
                    y += locate.getY() * currentTotalWeight;
                    totalWeight += currentTotalWeight;
                }
            }
        }
        x = x / totalWeight;
        y = y / totalWeight;

        return new Location(x, y);
    }

}
